/**
 * Copyright (C) 2005 the Lexi Project.
 *
 * This file is part of the Lexi document editor.
 *
 * Lexi is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 * 
 * Lexi is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with GNU Classpath; see the file COPYING.  If not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 *
 * Linking this library statically or dynamically with other modules is
 * making a combined work based on this library.  Thus, the terms and
 * conditions of the GNU General Public License cover the whole combination.
 */

package com.jmonkey.export;

import java.awt.Color;

/**
 * This class provides C-like formatting functions that allow programmers to
 * convert an integer or floating point number into a string with a specified,
 * width, precision and format. For instance this might be used to format
 * monetary data to two decimal places.
 * <P>
 * 
 * Each number is passed to a format() method along with formatting
 * instructions. The format() method returns a formatted string.
 * <P>
 * 
 * There are a number of possible things one can do when a number will not fit
 * inside the specified format. You can throw an exception, truncate the number,
 * return an error string, or expand the width. Here I've chosen to expand the
 * width.
 * <P>
 * 
 * The rounding of these precisions still needs work. Currently excess digits
 * are merely truncated.
 * <P>
 * 
 * Note: Many of the methods in this class are not used anywhere.
 * 
 * @Author: Brill Pappin <I>Portions from <CODE>cafeaulait.io.Formatter</CODE>
 *          by Elliotte Rusty Harold (elharo@sunsite.unc.edu)</I>
 */
public class Format {
  private static char[] lowercases = { '\000', '\001', '\002', '\003', '\004',
      '\005', '\006', '\007', '\010', '\011', '\012', '\013', '\014', '\015',
      '\016', '\017', '\020', '\021', '\022', '\023', '\024', '\025', '\026',
      '\027', '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
      '\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047', '\050',
      '\051', '\052', '\053', '\054', '\055', '\056', '\057', '\060', '\061',
      '\062', '\063', '\064', '\065', '\066', '\067', '\070', '\071', '\072',
      '\073', '\074', '\075', '\076', '\077', '\100', '\141', '\142', '\143',
      '\144', '\145', '\146', '\147', '\150', '\151', '\152', '\153', '\154',
      '\155', '\156', '\157', '\160', '\161', '\162', '\163', '\164', '\165',
      '\166', '\167', '\170', '\171', '\172', '\133', '\134', '\135', '\136',
      '\137', '\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
      '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157', '\160',
      '\161', '\162', '\163', '\164', '\165', '\166', '\167', '\170', '\171',
      '\172', '\173', '\174', '\175', '\176', '\177' };

  /**
   * The Separator cahr used in universal paths.
   */
  public static final char UNIVERSAL_SEPARATOR_CHAR = '/';

  /**
   * Do not allow instances to be created.
   */
  private Format() {
    super();
  }

  /**
   * fast lower case conversion. Only works on ascii (not unicode)
   * 
   * @author Jesper J�rgensen, Caput
   * @param s the string to convert
   * @return a lower case version of s
   */
  public static String asciiToLowerCase(String s) {
    char[] c = s.toCharArray();
    for (int i = c.length; i-- > 0;) {
      if (c[i] <= 127)
        c[i] = lowercases[c[i]];
    }
    return (new String(c));
  }

  /**
   * Converts a Color value to an HTML hex colour string in the format 
   * "#RRGGBB".  (The "alpha" component of the Color, if any, is ignored.)
   * 
   * @param color the Color to convert
   * @return the Color in #RRGGBB form.
   */
  public static final String colorToHex(Color color) {
    // FIXME - use a StringBuffer
    // FIXME - don't throw Error
    String colorStr = "#";

    // Red
    String str = Integer.toHexString(color.getRed());
    if (str.length() > 2)
      throw new Error("invalid red value");
    else if (str.length() < 2)
      colorStr += "0" + str;
    else
      colorStr += str;

    // Green
    str = Integer.toHexString(color.getGreen());
    if (str.length() > 2)
      throw new Error("invalid green value");
    else if (str.length() < 2)
      colorStr += "0" + str;
    else
      colorStr += str;

    // Blue
    str = Integer.toHexString(color.getBlue());
    if (str.length() > 2)
      throw new Error("invalid green value");
    else if (str.length() < 2)
      colorStr += "0" + str;
    else
      colorStr += str;
    return colorStr.toUpperCase();
  }

  /**
   * Create an escaped string that is compatible with the Property file
   * syntax.  FIXME - check where this method is used.  If it is really
   * used to write Property files (why?), then maybe we should escape
   * TAB, CR, NL etc like the Property file syntax does.
   * 
   * @param s the String to be escaped
   * @return String
   */

  public static final String escapeSafe(String s) {
    // FIXME - we know the String length ...
    StringBuffer sb = new StringBuffer();
    int counter = 0;
    for (counter = 0; counter < s.length(); counter++) {
      if (s.charAt(counter) == '\\') {
        sb.append("\\\\");
      }
      else {
        sb.append(s.charAt(counter));
      }
    }
    // FIXME - if we didn't insert any escapes, we could return the
    // original String.
    return sb.toString();
  }

  /**
   * Converts an array of hash bytes into a hex string.
   * 
   * @param bytes an array of hash bytes
   * @return the bytes encoded as a hexadecimal String.
   * @version 1.0
   * @author Julian Yip Aug. 1998 (Modified Brill Pappin Aug. 1998)
   */
  public static final String hashToHex(byte bytes[]) {
    StringBuffer sb = new StringBuffer(bytes.length * 2);
    for (int i = 0; i < bytes.length; i++) {
      if (bytes[i] < 0x10) {
        sb.append("0");
      }
      sb.append(Integer.toHexString(bytes[i]));
    }
    return sb.toString();
  }

  /**
   * Converts an HTML "#RRGGBB" hex string to a java.awt.Color
   * 
   * @param value an HTML #RRGGBB string
   * @return a Color with the corresponding RGB value.
   */
  public static final Color hexToColor(String value) {
    if (value.length() != 7) {
      // FIXME - never throw Error!
      throw new Error("invalid hex color string length");
    }
    else if (value.startsWith("#")) {
      Color c = Color.decode(value);
      return c;
    }
    // FIXME - surely this is wrong!
    return null;
  }

  /**
   * Converts a 2 character hex number into an integer.
   * 
   * @param hex the hex number to convert
   * @return the corresponding integer
   */
  public static final int hexToInt(String hex) {
    if (hex.length() != 2) {
      // FIXME - never throw Error!
      throw new Error("invalid hex string " + hex);
    }
    int pos1 = Character.digit(hex.charAt(0), 16) * 16;
    int pos0 = Character.digit(hex.charAt(1), 16);
    return (pos0 + pos1);
  }

  /**
   * Create a string from the 'input' string by removing all characters
   * that are not present in the 'wantedChars' string.
   *
   * @param input the input string
   * @param wantedChars the characters that we want to keep if they occur
   *        in 'input'. 
   * @return the 'input' string with unwanted characters removed. 
   * @author Ian MacMillan Aug. 1998
   * @see Format.remChars(String, String)
   */
  public final static String keepChars(String input, String wantedChars) {
    char[] cArr = new char[input.length()];
    char curChar = ' ';

    /* for each input char */
    int ox = 0;
    for (int n = 0; n < input.length(); n++) {
      /* is current char wanted */
      curChar = input.charAt(n);
      if (wantedChars.indexOf(curChar) >= 0) {
        cArr[ox] = curChar;
        ox++;
      }
    }

    return new String(cArr, 0, ox);
  }

  /**
   * Converts a universal path into a native path; i.e. by replacing 
   * all "/"s with the native file separator character.
   * 
   * @return java.lang.String
   * @param universalPath
   *          java.lang.String
   */
  public static final String nativePath(String universalPath) {
    // FIXME - this doesn't take account of Windows "drives".
    return universalPath.replace('/', java.io.File.separatorChar);
  }

  /**
   * Create a string from the 'input' string by removing all characters
   * that are present in the 'remChars' string.
   *
   * @param input the input string
   * @param remChars the characters that we want to remove from 'input'. 
   * @return the 'input' string with unwanted characters removed.
   * @see Format.keepChars(String, String)
   */
  public final static String removeChars(String input, String remChars) {
    char[] cArr = new char[input.length()];
    char curChar = ' ';
    /* for each input char */
    int ox = 0;
    for (int n = 0; n < input.length(); n++) {
      /* is current char wanted */
      curChar = input.charAt(n);
      if (remChars.indexOf(curChar) < 0) {
        cArr[ox] = curChar;
        ox++;
      }
    }
    return new String(cArr, 0, ox);
  }

  /**
   * Remove matching double-quotes from the beginning and end of a String. 
   * This method only remove quotes if they appear <i>at both ends</i> 
   * of the string.
   * <p>
   * For example <code>removeSurroundingQuotes("\""xxxxx\"")</code> returns 
   * "xxxxx", and code>removeSurroundingQuotes("\""xxxxx")</code> returns 
   * "\"xxxxx", 
   * 
   * @param s the String to be de-quoted
   * @return the input String with enclosing double quotes 
   */
  public static final String removeSurroundingQuotes(String s) {
    if (s.startsWith("\"") && s.endsWith("\"")) {
      return s.substring(1, s.length() - 1);
    } 
    else {
      return s;
    }
  }

  /**
   * Replace all instances of the substring 'sub' within the string 
   * 's' with a replacement 'with'.
   */
  public static String replace(String s, String sub, String with) {
    StringBuffer sb = new StringBuffer(s.length() * 2);
    int c = 0;
    int i = 0;
    while ((i = s.indexOf(sub, c)) != -1) {
      sb.append(s.substring(c, i));
      sb.append(with);
      c = i + sub.length();
    }
    if (c < s.length())
      sb.append(s.substring(c, s.length()));
    return sb.toString();
  }

  /**
   * Convert a native path into a universal one.
   * 
   * @return the corresponding universal path
   * @param nativePath a pathname in the platform's native pathname syntax.
   */
  public static final String universalPath(String nativePath) {
    String newPath;
    int index = nativePath.indexOf(java.io.File.separator);
    // FIXME - the following is surely wrong!
    if (index >= 0 && (index + 1) < nativePath.length()) {
      newPath = nativePath.substring(index + 1, nativePath.length());
    }
    else {
      newPath = nativePath;
    }
    return newPath.replace(java.io.File.separatorChar, '/');
  }
}
